#include "WProgram.h"
/* * * * * * * * * * * * * * * * * * * 
 GPS-A-Sketch version .06
 . . . . . . .
 GPS-A-Sketch Doodle Program v1
 
 Program collects LAT & LON data 
 does math & draws path to the screen
 Button A has no function
 Button B Erases the screen
 . . . . . . . .
 Project started August, 2008
 by Wil Lindsay
 For the Hacktory  
 http://thehacktory.org
 
 NMEA read code based loosely on code by 
 Igor Gonz\u00e1lez Mart\u00edn from 05-04-2007   
 
 * * * * * * * * * * * * * * * * * * */

#include <SoftwareSerial.h>
#include <string.h>
#include <ctype.h>

#define buttonPinA 3
#define buttonPinB 4
#define LCDrxPin 5
#define LCDtxPin 6
#define ledPin 13
#define GPStxPin 7
#define GPSrxPin 8


// set up 2 new serial ports for LCD and GPS
void setup();
void loop();
int NMEA_fieldSize(int fieldNumber);
void toggle(int pinNum);
void LCD_init();
void LCD_line(int x1,int y1,int x2,int y2);
void LCD_wake();
void LCD_sleep();
SoftwareSerial LCDSerial =  SoftwareSerial(LCDrxPin, LCDtxPin);
SoftwareSerial GPSSerial =  SoftwareSerial(GPSrxPin, GPStxPin);

byte pinState = 0;
byte buttonStateA = 0;
byte buttonStateB = 0;

int byteGPS=-1;
char NMEA_buffer[300] = "";                          //buffer for NMEA data
char NMEA_header[7] = "$GPRMC";
int cont=0;
int bien=0;
int conta=0;
int indices[12];
int buttonFlag = 0;
int buttonState = 0;

char latHolder[4];              //to hold LAT char 6 & 7 (decimal of minutes)
char lonHolder[4];              //to hold LON char 7 & 8 (decimal of minutes)

int latRaw = 0;                    //Holds Lat & Lon data
int lonRaw = 0;
int xPos = 42;                      //Holds current screen location
int yPos = 24;                      
int lastXPos = 42;                  //Holds last screen location
int lastYPos = 24;
int startingLat = 0;                //holds the origin LAT & LON
int startingLon = 0;
int diffLat = 0;                    //how far user has moved LAT & LON
int diffLon = 0;
int originFlag = 1;              //If true (1), find starting point for drawing

void setup() {
  // define pin modes for GPS, LCD & led pins:
  pinMode(buttonPinA, INPUT);
  pinMode(buttonPinB, INPUT);
  pinMode(LCDrxPin, INPUT);
  pinMode(LCDtxPin, OUTPUT);
  pinMode(GPSrxPin, INPUT);
  pinMode(GPStxPin, OUTPUT);
  pinMode(ledPin, OUTPUT);

  // set the data rate for the SoftwareSerial port
  LCDSerial.begin(9600);
  GPSSerial.begin(4800);
  //initialize SGD_A LCD unit
  LCD_init();
  delay(1000);
  LCD_sleep();
  Serial.begin(9600);
  for (int i=0;i<300;i++){       // Initialize a buffer for received data
    NMEA_buffer[i]=' ';
  }
  for (int i=0;i<4;i++){        //Init the holders for capture data
    latHolder[i]=' ';
    lonHolder[i]=' ';
  } 
}
void loop() {
  digitalWrite(ledPin, LOW);
  byteGPS=GPSSerial.read();         // Read a byte from the serial port
  if (byteGPS == -1) {              // See if the port is empty
    delay(100); 
  } 
  else {
    NMEA_buffer[conta]=byteGPS;        // If there is serial port data, put it in the buffer
    conta++;                      
    if (byteGPS==13){            // If the received byte is = to 13, end of transmission
      cont=0;
      bien=0;
      for (int i=1;i<7;i++){     // Verifies if the received command starts with $GPRMC header
        if (NMEA_buffer[i]==NMEA_header[i-1]){
          bien++;
        }
      }
      if(bien==6){               // If yes, continue and process the data
        for (int i=0;i<300;i++){
          if (NMEA_buffer[i]==','){    // check for the position of the  "," separator
            indices[cont]=i;
            cont++;
          }
          if (NMEA_buffer[i]=='*'){      //check for the * -- starting checksum
            indices[11]=i+3;
            cont++;
          }
        }
        //check for LAT data
        if ((NMEA_buffer[(indices[1])+1]) == 65){        //Status case : ascii 65 = "A" not "V"
          latHolder[0] = NMEA_buffer[indices[2]+6];     //load data 6 places in  into holder array
          latHolder[1] = NMEA_buffer[indices[2]+7];
          latRaw = atoi(latHolder);                   //convert from ascii to int
          //check for LON data
          lonHolder[0] = (NMEA_buffer[indices[4]+7]);     //load data 7 places in into holder array
          lonHolder[1] = NMEA_buffer[indices[4]+8];
          lonRaw = atoi(lonHolder);                   //convert from ascii to int

          /*
          //Serial.println(NMEA_buffer);      // to debug to terminal
           Serial.print(latRaw);
           Serial.print(":");
           Serial.println(lonRaw); 
           */

          if (originFlag){                    //determine the new drawing beginning
            startingLat = latRaw;
            startingLon = lonRaw;
            originFlag = 0;  
          }

          diffLat = latRaw - startingLat;    //determine the distance from center of drawing
          diffLon = lonRaw - startingLon;

          if (diffLat > 90){  //if user moved over a minute line
            startingLat = startingLat + 100;      
            diffLat = latRaw - startingLat;
          }
          if (diffLat < -90){ //if user moved over a minute line
            startingLat = startingLat - 100;
            diffLat = latRaw - startingLat;
          }
          if (diffLon > 90){  //if user moved over a minute line
            startingLon = startingLon + 100;
            diffLon = lonRaw - startingLon;
          }
          if (diffLon < -90){ //if user moved over a minute line
            startingLon = startingLon - 100;
            diffLon = lonRaw - startingLon;
          }

        } 
        else {                              //Status cas "V" - no reception
          /*
          Serial.println(NMEA_buffer[indices[1]+1],DEC);    //to debug to terminal
           Serial.println ("Searching for Signal"); 
           */
          diffLat = 0;                      //no reception == no change in drawing
          diffLon = 0;
        }

        //convert Lat & LON data to useful screen X & Y
        xPos = 42 - diffLon;      //track the motion from centerpoint to draw
        yPos = 24 - diffLat;   
     
        if (xPos > 84){xPos = 84;}  //edge detect screen X & Y and lock to edge
        if (xPos < 0) {xPos = 0;}
        if (yPos > 48) {yPos = 48;}
        if (yPos < 0 ) {yPos = 0;}
        
        LCD_line(lastXPos,lastYPos,xPos,yPos);
        lastXPos = xPos;      //save coordinates for next draw
        lastYPos = yPos;
        /*                                     Debug to Terminal
        Serial.print(xPos);
        Serial.print(":");
        Serial.println(diffLon); 
        Serial.print(yPos); 
        Serial.print(":");
        Serial.println(diffLat); 
        */
      }
      conta=0;                    // Reset the buffer
      for (int i=0;i<300;i++){    //  
        NMEA_buffer[i]=' ';        
      }
      for (int i=0;i<4;i++){
        latHolder[i]=' ';
        lonHolder[i]=' ';
      }    
    }
  }

  //* * * * * * * * Button Status * * * * * * * *
  buttonStateA = digitalRead(buttonPinA);
  buttonStateB = digitalRead(buttonPinB);
  if (buttonStateA == HIGH){
    //stop drawing
  }
  if (buttonStateB == HIGH){    //button B clears screen and recenters X,Y
    delay (100);
    LCDSerial.print('N',BYTE);
    xPos = 42;                //start in center
    yPos = 24;
    originFlag = 1;          // find new starting LAT & LON for new drawing
  }
  //* * * * * * * End Button Status * * * * * * * *

}

//********************************NMEA data parse
/*  as follows:  
 
 case : content : format : fieldsize used
 
 0:  Time in UTC             : HhMmSs.sss         : 10
 1:  Status (A=OK,V=KO)      : A or V             : 1
 2:  Latitude                : ddmm.mmmm          : 9
 3:  Direction (N/S)         : N or S             : 1
 4:  Longitude               : dddmm.mmmm         : 10
 5:  Direction (E/W)         : E or W             : 1
 6:  Velocity in knots       : k.kk               : 4
 7:  Heading in degrees      : ddd.dd             : 6
 8:  Date UTC                : DdMmYy             : 6
 9:  Magnetic Var deg. (E/W) : none               : 0          // Mag Var not on EM-406A & no "Mode" sent
 10: CheckSum                : *Dd                : 3          // Message terminated with <CR> <LF> (ASCII <10><13>)
 
 */


//********************************array Field Size for NMEA field data
int NMEA_fieldSize(int fieldNumber) {
  return (indices[fieldNumber+1]-indices[fieldNumber]-1);     //how many spaces between the commas
}



//********************************toggle function for the LED
void toggle(int pinNum) {
  // set the LED pin using the pinState variable:
  digitalWrite(pinNum, pinState); 
  // if pinState = 0, set it to 1, and vice versa:
  pinState = !pinState;
}

// ******************************* SGD-A LCD initialization

void LCD_init(){
  toggle(13);
  delay(1000);       //sleep & awake to initialize LCD system
  LCD_sleep();
  delay(1000);
  LCD_wake();
  delay(100);
  LCDSerial.print('N',BYTE);               //clear screen
  delay(100);
  LCDSerial.print('S',BYTE);               //print logo
  LCDSerial.print(18,BYTE);
  LCDSerial.print("GPS-A-Sketch! v.06");

  delay(100);
  LCDSerial.print('T',BYTE);               //2 tone chirp
  LCDSerial.print(51,BYTE);
  LCDSerial.print(12,BYTE);
  LCDSerial.print(2,BYTE);
  delay(100);
  LCDSerial.print('T',BYTE);
  LCDSerial.print(12,BYTE);
  LCDSerial.print(249,BYTE);
  LCDSerial.print(2,BYTE);
  delay (2000);
  LCDSerial.print('N',BYTE);               //clear screen
  toggle(13);
}
//********************************Line Function for LCD
void LCD_line(int x1,int y1,int x2,int y2){
  delay(100);
  LCDSerial.print('P',BYTE);            //startpoint x & y
  LCDSerial.print(x1,BYTE);
  LCDSerial.print(y1,BYTE);

  delay(100);
  LCDSerial.print('L',BYTE);            //endpoint x & y
  LCDSerial.print(x2,BYTE);
  LCDSerial.print(y2,BYTE);
  delay(100);


}
//******************************** LCD wake from sleep
void LCD_wake() {
  char result = 'Z';
  do {
    LCDSerial.print('X', BYTE);        //ping command - ping LCD until it responds
    result = LCDSerial.read();
    toggle(13);
  } 
  while (result != 'X') ;           //with an 'X'
}
//*************************to enter LCD sleep mode
void LCD_sleep() {
  LCDSerial.print('Z', BYTE);
}


int main(void)
{
	init();

	setup();
    
	for (;;)
		loop();
        
	return 0;
}

